package com.lixin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lixin.entity.GroupInfo;
import com.lixin.entity.GroupMember;
import com.lixin.exception.GroupDisbandedException;
import com.lixin.mapper.GroupInfoMapper;
import com.lixin.redis.RedisKey;
import com.lixin.redis.ZSetRedisUtil;
import com.lixin.service.IGroupInfoService;
import com.lixin.service.IGroupMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author lixin
 * @since 2023-08-08
 */
@Service
public class GroupInfoServiceImpl extends ServiceImpl<GroupInfoMapper, GroupInfo> implements IGroupInfoService {

    public static final String groupName = "";
    @Autowired
    private IGroupMemberService iGroupMemberService;
    @Autowired
    private ZSetRedisUtil zSetRedisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public GroupInfo createGroup(Integer userId) {
        //创建组
        GroupInfo groupInfo = new GroupInfo();
        Date date = new Date();
        groupInfo.setCreatedAt(date);
        groupInfo.setCreatedBy(userId);
        groupInfo.setGroupName(groupName);
        this.baseMapper.insert(groupInfo);
        iGroupMemberService.addOrUpdate(iGroupMemberService.builderMember(groupInfo.getGroupId(), true, userId, date));
        zSetRedisUtil.zAdd(RedisKey.getKey(RedisKey.GROUP_MEMBER, groupInfo.getGroupId()), userId, date.getTime());
        return groupInfo;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public GroupMember joinGroup(Integer userId, Integer groupId) {
        if (!isInGroup(groupId, userId)) {
            Date date = new Date();
            GroupMember groupMember = iGroupMemberService.addOrUpdate(iGroupMemberService.builderMember(groupId, true, userId, date));
            zSetRedisUtil.zAdd(RedisKey.getKey(RedisKey.GROUP_MEMBER, groupId), userId, date.getTime());
            return groupMember;
        }
        //in group
        return null;
    }

    /**
     * 主要是为了后期加入缓存
     *
     * @param groupId
     * @return
     */
    @Override
    public GroupInfo selectById(Integer groupId) {
        return getById(groupId);
    }

    @Override
    public Boolean isInGroup(Integer groupId, Integer userId) {
        return checkAndSyncGroupMembers(groupId).contains(userId);
    }

    /**
     * 这个函数的作用单纯的是获取所有群聊成员 所以一些必要的检查还是需要存在的。
     * 虽然目前在上层的调用已经做过部分重复检查
     *
     * @param groupId
     * @return
     */
    @Override
    public Set<Object>  getGroupMember(Integer groupId) {
        return checkAndSyncGroupMembers(groupId);
    }

    private Set<Object> checkAndSyncGroupMembers(Integer groupId) {
        Set<Object> groupMemberSet = zSetRedisUtil.zRange(RedisKey.getKey(RedisKey.GROUP_MEMBER, groupId), 0, -1);
        if (groupMemberSet.isEmpty()) {
            // 可能缓存被淘汰了，也可能群聊解散了
            // 因为是简易的项目，目前不留标记，如果存在群聊解散的情况就把主表跟字表的数据都删除
            List<GroupMember> groupMembers = iGroupMemberService.selectMemberByGroupId(groupId);
            if (groupMembers.isEmpty()) {
                throw new GroupDisbandedException();
            }
            synchronizeGroupMembersToCache(groupId, groupMembers);
            return zSetRedisUtil.zRange(RedisKey.getKey(RedisKey.GROUP_MEMBER, groupId), 0, -1);
        }
        return groupMemberSet;
    }

    @Override
    public Boolean isAdmin(Integer userId) {
        return iGroupMemberService.selectByUserId(userId).getIsAdmin();

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer deleteByGroupId(Integer groupId) {
        int count = iGroupMemberService.deleteByGroupId(groupId);
        deleteGroupMemberCache(groupId);
        this.baseMapper.deleteById(groupId);
        return count;
    }

    private void deleteGroupMemberCache(Integer groupId) {
        zSetRedisUtil.deleteKey(RedisKey.getKey(RedisKey.GROUP_MEMBER, groupId));
    }

    private void synchronizeGroupMembersToCache(Integer groupId, List<GroupMember> groupMembers) {
        groupMembers.forEach(item -> zSetRedisUtil.zAdd(RedisKey.getKey(RedisKey.GROUP_MEMBER, groupId), item.getUserId(), item.getJoinTime().getTime()));
    }


}
